#ifndef SPCPCLASS_H
#define SPCPCLASS_H
/*
*   该文件夹主要用于处理上下位机进行通信的协议处理，主要负责数据封装以及协议拆解，数据验证等接口的实现
*/

#include <QObject>
#include <QDataStream>

#include "ENUM/enum.h"
#include "DebugPrint/myDebugPrint.h"

#define PHKindType       uint8_t     // 报文类型
#define PHIsOffsetType   uint8_t     // 偏移标志位
#define PHSeekNumberType uint32_t    // 数据偏移（这里也可以直接设置为与数据长度一致，当偏移标志位被设置时，当前数据报的实际偏移值应该为该值*4后的结果）
#define PHSpaceType      uint8_t     // 滞留位
#define PHDataNumberType uint8_t     // 数据长度
#define PHCheckSumType   uint16_t    // 校验和
#define PDataType        uchar*      // 数据内容
#define DataType         uchar       // 数据内容

#define PHKindLoc      0      // 报文类型在数据报头中的偏移位置
#define PHIsOffsetLoc  1      // 偏移标志位在数据报头中的偏移位置
#define PHSeekNumLoc   2      // 数据偏移量在数据报头中的偏移位置
#define PHDataNumLoc   6      // 数据长度在数据报头中的偏移位置
#define PHSpaceLoc     7      // 滞留标志位在数据报头中的偏移位置
#define PHCheckSumLoc  8      // 校验和标志位在数据报头中的偏移位置
#define PDataLoc       10     // 有效数据在数据报头中的偏移位置

#define RProtocolHeadSize() (static_cast<size_t>(sizeof(PHKindType) + sizeof(PHIsOffsetType) + sizeof(PHSeekNumberType) + sizeof(PHDataNumberType) + sizeof(PHCheckSumType) + sizeof(PHSpaceType)))

class SPCPCLass : public QObject {
    Q_OBJECT
public:
    explicit SPCPCLass(QObject* parent = nullptr);
};

class SPCPClass
{
public:
    SPCPClass(ESPCPHeaderKind PHK = ESPCPHeaderKind::NullKind, EISorNO PHI = EISorNO::null, PHSeekNumberType PHSN = 0, PHDataNumberType PHDN = 0, PHCheckSumType PHCS = 0, PDataType PD = nullptr);

    /**
     @interpret   设置数据报头中的m_ProtocolHeaderKind
     @param       prorocolHeaderKind*: 当前数据报类型; kind: 表示当前需要获取还是修改变量
     @return      true-成功; false-失败
     */
    bool PHKind(ESPCPHeaderKind* prorocolHeaderKind, ParameterKind kind);

    /**
     @interpret   设置数据报报头中的m_ProtocolHeaderIsOffset
     @param       isOffset: 当前数据报是否是拆包过
     @return      true-成功; false-失败
     */
    bool PHIsOffset(EISorNO* isOffset, ParameterKind kind);

    /**
     @interpret   设置数据偏移值m_ProtocolHeaderSeekNumber
     @param       number: 当前包偏移的数值
     @return      true-成功; false-失败
     */
    bool PHSeekNum(PHSeekNumberType* number, ParameterKind kind);

    /**
     @interpret   设置有效数据的长度m_ProtocolHeaderDataNumber
     @param       data: 当前数据报中有效数据的长度
     @return      true-成功;false-失败
     */
    bool PHDataNumber(PHDataNumberType* data, ParameterKind kind);

    /**
     @interpret   设置校验和的值m_ProtocolHeaderCheckSum
     @param       checkSum: 校验和值
     @return      true-成功;false-失败
     */
    bool PHCheckSum(PHCheckSumType* checkSum, ParameterKind kind);

    /**
     @interpret   设置当前数据包中有效数据m_ProtocolData
     @param       data: 表示有效数据的句柄; dataLen: 表示当前有效数据数据长度（在设置有效数据时需要将有效数据长度传入，且在获取有效数据句柄时也会返回该有效数据的长度;-1表示获取失败）
     @return      true-成功;false-失败
     */
    bool Data(PDataType data, ParameterKind kind, int* dataLen = 0);

    /**
     @interpret   检查SPCP是否具备发送的条件
     @param       errMsg: 返回错误信息
     @return      true-具备;false-不具备
     */
    bool CheckSPCPInfo(QString& errMsg);

    /**
     @interpret   将当前结构体存储的内容都转换为QByteArray返回，方便后后面对QByteArray进行发送等处理逻辑。
     @param       无。
     @return      QByteArray: 当前结构的所有数据按照协议报头的格式进行存储。
     */
    QByteArray RClassToByteArray();

    /**
     @interpret   通过获取对应数据报的QByteArray计算校验和的值（并插入到数据报中）
     @param       无。
     @return      PHCheckSumType: 返回对应校验和的值。
     */
    PHCheckSumType ComputCheckSum(QByteArray& array);

    /**
     @interpret   通过获取的QByteArray，并进行解析将解析数据保存至当前类型中
     @param       Array: 需要进行解析的数据; errMsg: 用于返回错误信息。
     @return      true-解析成功;false-解析失败
     */
    bool DataToClass(QByteArray& Array, QString* errMsg);
protected:
    PHKindType        m_ProtocolHeaderKind;            // 报文类型 (表示当前报文的类型,占8bit)
    PHIsOffsetType    m_ProtocolHeaderIsOffset;        // 偏移情况 (表示当前报文是否被拆包,占8bit)
    PHSeekNumberType  m_ProtocolHeaderSeekNumber;      // 数据偏移 (表示当前数据偏移量:一般记录当前协议第一个有效数据所在总包的位置)
    PHDataNumberType  m_ProtocolHeaderDataNumber;      // 数据长度 (表示当前包有效数据长度:如果偏移情况被设置为1，当前数据报实际有效数据为数据长度*4【所以拆出的包只能是4byte的倍数】，当偏移情况为0时:该值就为有效数据的真实值)
    PHCheckSumType    m_ProtocolHeaderCheckSum;        // 校验和   (表示当前数据报的校验和，保证当前数据报的完整性)
    PDataType         m_ProtocolData;                  // 数据内容 (表示有效数据的句柄)
};
#endif // SPCPCLASS_H
